import orbitControls from './OrbitControls'
import * as THREE from 'three'
import { Vector2, Vector3 } from 'three';

import { Geometry } from './Geometry';
import { Font, FontLoader } from 'three/examples/jsm/loaders/FontLoader.js';


import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js';

enum HorizontalTextAlignment {
    Left = 0,
    Right = 1,
    Center = 2
}

enum VerticalTextAlignment {
    Top = 0,
    Bottom = 1,
    Middle = 2
}


interface BcsDrawing2dData {
    /**
     * 空字符串
     */
    comment: string;
    /*** 2d坐标*/
    drawingNamePosition: Point2d;
    /*** 数据*/
    entities: Entity[];
    /*** 项目名称*/
    name: string;
    /*** 图例尺寸*/
    scale: number;
    /*** 布尔值*/
    scaleVisible: boolean;
    /*** 类型*/
    type: string;
    /*** 版本*/
    version: string;
    /** 可能是dxfparse里面带的*/
    blocks?: any

    /** 以前的dxf代码里面的，可能是颜色表 */
    tables?: any;
}
interface Point2d {
    X: number;
    Y: number;
}



interface Line2d {
    startPoint: Point2d;
    endPoint: Point2d;
}
interface dims_type {
    min: point_bool_number_type,
    max: point_bool_number_type
}

interface point_bool_number_type {
    x: boolean | number,
    y: boolean | number,
    z: boolean | number;
}
interface Point3d {
    X: number;
    Y: number;
    Z: number;
}
interface Entity {
    type: string;
    layer?: any;
    color?: any;
}
interface TSLine3d extends Entity {
    EndPoint: any,
    StartPoint: any,
    LinetypeName: string,

}
interface Arc extends Entity {
    // startAngle: number;
    // endAngle: number;
    Radius: number;
    Center: any;
    StartAngle: number;
    EndAngle: number;
}

interface Circle extends Entity {
    Radius: number;
    Center: any;
}

interface Line extends Entity {
    lineType: any;
}

interface Polyline extends Entity {
    points: any[],
    closed?: boolean
}
interface TSAlignedDimension extends Entity {
    /** 尺寸引出线1的起点 */
    XLine1Point: Point3d;
    /** 尺寸引出线2的起点 */
    XLine2Point: Point3d;
    /** 尺寸横线上的一点 */
    DimLinePoint: Point3d;
    /** 尺寸文字，如果为空的话，表示采用实际测量得到的值 */
    DimensionText: string;
}

interface TSRotatedDimension extends Entity {

    /** 尺寸引出线1的起点 */
    XLine1Point: Point3d;
    /** 尺寸引出线2的起点 */
    XLine2Point: Point3d;
    /** 尺寸横线上的一点 */
    DimLinePoint: Point3d;
    /** 尺寸文字，如果为空的话，表示采用实际测量得到的值 */
    DimensionText: string;
    /** 旋转角，弧度 */
    Rotation: number;
}

interface TSText extends Entity {
    TextString: string;
    Position: Point2d;
    HorizontalMode: HorizontalTextAlignment;
    VerticalMode: VerticalTextAlignment;
    Rotation: number;
    Height: number;
}


let data_bcs;
const OrbitControls = orbitControls(THREE)

/**
 * Returns the angle in radians of the vector (p1,p2). In other words, imagine
 * putting the base of the vector at coordinates (0,0) and finding the angle
 * from vector (1,0) to (p1,p2).
 * @param  {Object} p1 start point of the vector
 * @param  {Object} p2 end point of the vector
 * @return {Number} the angle
 */
//THREE.Math.angle2 = function (p1, p2) {
const angle2 = function (p1: Vector2, p2: Vector2) {
    var v1 = new Vector2(p1.x, p1.y);
    var v2 = new Vector2(p2.x, p2.y);
    v2.sub(v1); // sets v2 to be our chord
    v2.normalize();
    if (v2.y < 0) return -Math.acos(v2.x);
    return Math.acos(v2.x);
};

//THREE.Math.polar = function (point, distance, angle) {
const polar = function (point: Vector2, distance: number, angle: number) {
    const x = point.x + distance * Math.cos(angle);
    const y = point.y + distance * Math.sin(angle);
    const result = new Vector2(x, y);
    return result;
};

/**
 * Calculates points for a curve between two points
 * @param startPoint - the starting point of the curve
 * @param endPoint - the ending point of the curve
 * @param bulge - a value indicating how much to curve
 * @param segments - number of segments between the two given points
 */
/**
 * 创建弧线几何体并继承自 Geometry 类
 */
class BulgeGeometry extends Geometry {

    startPoint: Vector2;
    endPoint: Vector2;
    bulge: number;
    segments: number;
    vertices: Vector3[] = [];

    /**
     * 构造函数
     * @param {Vector2} startPoint 起点
     * @param {Vector2} endPoint 终点
     * @param {number} bulge 弧度
     * @param {number} segments 分段数
     */
    constructor(startPoint: Vector2, endPoint: Vector2, bulge: number, segments?: number) {
        super();
        var vertex, i,
            center, p0, p1, angle,
            radius, startAngle,
            thetaAngle;

        this.startPoint = p0 = startPoint ? new THREE.Vector2(startPoint.x, startPoint.y) : new THREE.Vector2(0, 0);
        this.endPoint = p1 = endPoint ? new THREE.Vector2(endPoint.x, endPoint.y) : new THREE.Vector2(1, 0);
        this.bulge = bulge = bulge || 1;

        angle = 4 * Math.atan(bulge);
        radius = p0.distanceTo(p1) / 2 / Math.sin(angle / 2);
        center = polar(startPoint, radius, angle2(p0, p1) + (Math.PI / 2 - angle / 2));

        this.segments = segments = segments || Math.max(Math.abs(Math.ceil(angle / (Math.PI / 18))), 6); // By default want a segment roughly every 10 degrees
        startAngle = angle2(center, p0);
        thetaAngle = angle / segments;

        this.vertices.push(new THREE.Vector3(p0.x, p0.y, 0));

        for (i = 1; i <= segments - 1; i++) {
            vertex = polar(center, Math.abs(radius), startAngle + thetaAngle * i);
            this.vertices.push(new THREE.Vector3(vertex.x, vertex.y, 0));
        }
    }
}

//BulgeGeometry.prototype = Object.create(Geometry.prototype);
var font: Font;
var scene = new THREE.Scene();
var $parent: HTMLElement;
var width: number;
var height: number

export async function loadFont(fontSrc) {
    const loader = new FontLoader();
    try {
        font = await loader.loadAsync(fontSrc);
        return font;
    } catch (error) {
        throw new Error("Failed to load font: " + error);
    }
}

export async function initialize(w: number, h: number, fontSrc: string): Promise<any> {
    $parent = document.getElementById("bcs_three_div") as HTMLElement;
    width = w;
    height = h;
}


export async function loadData(data: BcsDrawing2dData, callback: Function, parent: HTMLElement, fontSrc: string) {
    scene.children = []
    data_bcs = data
    if (font == undefined) {
        var loader = new FontLoader();
        try {
            font = await loader.loadAsync(fontSrc);
        } catch (error) {
            throw new Error("Failed to load font: " + error);
        }
    }
    var res_entities = {} as any, layer_num = {};
    var i, objs: THREE.Object3D[];
    let dims: dims_type = {
        min: { x: false, y: false, z: false },
        max: { x: false, y: false, z: false }
    };
    for (i = 0; i < data.entities.length; i++) {
        const entity = data.entities[i];
        objs = drawEntity(entity, data, font);
        for (let j = 0; j < objs.length; j++) {
            if (objs[j]) {
                const bbox = new THREE.Box3().setFromObject(objs[j]);
                if (bbox.min.x && ((dims.min.x === false) || (dims.min.x as number > bbox.min.x))) dims.min.x = bbox.min.x;
                if (bbox.min.y && ((dims.min.y === false) || (dims.min.y as number > bbox.min.y))) dims.min.y = bbox.min.y;
                if (bbox.min.z && ((dims.min.z === false) || (dims.min.z as number > bbox.min.z))) dims.min.z = bbox.min.z;
                if (bbox.max.x && ((dims.max.x === false) || (dims.max.x as number < bbox.max.x))) dims.max.x = bbox.max.x;
                if (bbox.max.y && ((dims.max.y === false) || (dims.max.y as number < bbox.max.y))) dims.max.y = bbox.max.y;
                if (bbox.max.z && ((dims.max.z === false) || (dims.max.z as number < bbox.max.z))) dims.max.z = bbox.max.z;
                renderGeometry(objs[j], layer_num, res_entities, scene)
            }
            objs[j] = {} as any;
        }
    }
    //如果宽高为0,则重新赋值
    width = width || $parent.offsetWidth;
    height = height || $parent.offsetHeight;
    //计算经宽高比
    var aspectRatio = width / height;

    //获取矩形的最大坐标与最小坐标
    var upperRightCorner = { x: dims.max.x, y: dims.max.y };
    var lowerLeftCorner = { x: dims.min.x, y: dims.min.y };


    // 找出适当的视口范围
    var vp_width = upperRightCorner.x as number - (lowerLeftCorner.x as number);
    var vp_height = upperRightCorner.y as number - (lowerLeftCorner.y as number);
    var center = {
        x: vp_width / 2 + (lowerLeftCorner.x as number),
        y: vp_height / 2 + (lowerLeftCorner.y as number)
    };

    // 将对象放到元素中
    var extentsAspectRatio = Math.abs(vp_width / vp_height);
    if (aspectRatio > extentsAspectRatio) {
        vp_width = vp_height * aspectRatio;
    } else {
        vp_height = vp_width / aspectRatio;
    }

    var viewPort = {
        bottom: -vp_height / 2,
        left: -vp_width / 2,
        top: vp_height / 2,
        right: vp_width / 2,
        center: {
            x: center.x,
            y: center.y
        }
    };
    //场景的大小
    var camera = new THREE.OrthographicCamera(viewPort.left * 1.5, viewPort.right * 1.5, viewPort.top * 1.5, viewPort.bottom * 1.5, 1, 19);
    camera.position.z = 10;
    camera.position.x = viewPort.center.x;
    camera.position.y = viewPort.center.y;

    var pixelRatio = window.devicePixelRatio || 1;
    //创建一个渲染器对象传递的参数是否开启抗锯齿
    var renderer = new THREE.WebGLRenderer({ antialias: true });
    //设置渲染器的像素比
    renderer.setPixelRatio(pixelRatio);

    //设置渲染器大小
    renderer.setSize(width, height);
    //清除颜色并设置新的颜色(颜色,清除颜色的透明度)
    renderer.setClearColor(0xfffffff, 1);
    $parent.append(renderer.domElement);


    callback();
    var controls = new OrbitControls(camera, parent);
    controls.target.x = camera.position.x;
    controls.target.y = camera.position.y;
    controls.target.z = 0;
    controls.zoomSpeed = 3;
    //鼠标的点击，滚轮的放大缩小
    controls.addEventListener('change', () => {
        renderer.render(scene, camera);
    });
    //但是这两句注释也没有影响
    renderer.render(scene, camera);
    controls.update();
    return true
}


/**
 * Viewer class for a dxf object.
 * @param {Object} data - the dxf object
 * @param {Object} parent - the parent element to which we attach the rendering canvas
 * @param {Number} width - width of the rendering canvas in pixels
 * @param {Number} height - height of the rendering canvas in pixels
 * @param {Object} font - a font loaded with THREE.FontLoader 
 * @constructor
 */
export function Viewer(data: BcsDrawing2dData, parent: any, width: number, height: number, callback: Function, fontSrc: string) {
    data_bcs = data
    var $parent = document.getElementById("dxf-view") as HTMLElement;
    var font;
    var loader = new FontLoader();
    loader.load(fontSrc, function (response) {
        font = response;
        createLineTypeShaders(data);
        var scene = new THREE.Scene();
        var res_entities = {} as any, layer_num = {};
        // Create scene from dxf object (data)
        var i, objs: THREE.Object3D[];
        let dims: dims_type = {
            min: { x: false, y: false, z: false },
            max: { x: false, y: false, z: false }
        };
        for (i = 0; i < data.entities.length; i++) {
            const entity = data.entities[i];
            objs = drawEntity(entity, data, font);
            for (let j = 0; j < objs.length; j++) {
                if (objs[j]) {
                    const bbox = new THREE.Box3().setFromObject(objs[j]);
                    if (bbox.min.x && ((dims.min.x === false) || (dims.min.x as number > bbox.min.x))) dims.min.x = bbox.min.x;
                    if (bbox.min.y && ((dims.min.y === false) || (dims.min.y as number > bbox.min.y))) dims.min.y = bbox.min.y;
                    if (bbox.min.z && ((dims.min.z === false) || (dims.min.z as number > bbox.min.z))) dims.min.z = bbox.min.z;
                    if (bbox.max.x && ((dims.max.x === false) || (dims.max.x as number < bbox.max.x))) dims.max.x = bbox.max.x;
                    if (bbox.max.y && ((dims.max.y === false) || (dims.max.y as number < bbox.max.y))) dims.max.y = bbox.max.y;
                    if (bbox.max.z && ((dims.max.z === false) || (dims.max.z as number < bbox.max.z))) dims.max.z = bbox.max.z;
                    renderGeometry(objs[j], layer_num, res_entities, scene)
                }
                objs[j] = {} as any;
            }
        }
        var line_material = new THREE.LineBasicMaterial({ vertexColors: false })
        var dash_material = new THREE.LineDashedMaterial({ vertexColors: false, dashSize: 3, gapSize: 1, linewidth: 2 })
        for (var key in res_entities) {
            // res_entities[key]
            var mesh, ent = new THREE.BufferGeometry();

            if (key == 'Dashed') {
                ent.setIndex(res_entities[key].indices);
                //ent.attributes
                ent.setAttribute('position', new THREE.Float32BufferAttribute(res_entities[key].points, 3));
                ent.setAttribute('color', new THREE.Float32BufferAttribute(res_entities[key].colors, 3));
                ent.computeBoundingBox();
                mesh = new THREE.LineSegments(ent, dash_material);
                mesh.computeLineDistances();
            }
            else {

                ent.setIndex(res_entities[key].indices);
                ent.setAttribute('position', new THREE.Float32BufferAttribute(res_entities[key].points, 3));
                ent.setAttribute('color', new THREE.Float32BufferAttribute(res_entities[key].colors, 3));
                //ent.computeBoundingSphere();
                mesh = new THREE.LineSegments(ent, line_material);
            }

            mesh.userData = { layer: key }
            scene.add(mesh);
        }
        width = width || $parent.offsetWidth;
        height = height || $parent.offsetHeight;
        var aspectRatio = width / height;

        var upperRightCorner = { x: dims.max.x, y: dims.max.y };
        var lowerLeftCorner = { x: dims.min.x, y: dims.min.y };

        // Figure out the current viewport extents
        var vp_width = upperRightCorner.x as number - (lowerLeftCorner.x as number);
        var vp_height = upperRightCorner.y as number - (lowerLeftCorner.y as number);
        var center = {
            x: vp_width / 2 + (lowerLeftCorner.x as number),
            y: vp_height / 2 + (lowerLeftCorner.y as number)
        };

        // Fit all objects into current ThreeDXF viewer
        var extentsAspectRatio = Math.abs(vp_width / vp_height);
        if (aspectRatio > extentsAspectRatio) {
            vp_width = vp_height * aspectRatio;
        } else {
            vp_height = vp_width / aspectRatio;
        }

        var viewPort = {
            bottom: -vp_height / 2,
            left: -vp_width / 2,
            top: vp_height / 2,
            right: vp_width / 2,
            center: {
                x: center.x,
                y: center.y
            }
        };
        var camera = new THREE.OrthographicCamera(viewPort.left * 1.5, viewPort.right * 1.5, viewPort.top * 1.5, viewPort.bottom * 1.5, 1, 19);
        camera.position.z = 10;
        camera.position.x = viewPort.center.x;
        camera.position.y = viewPort.center.y;

        var pixelRatio = window.devicePixelRatio || 1;
        var renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setPixelRatio(pixelRatio);

        renderer.setSize(width, height);
        renderer.setClearColor(0xfffffff, 1);

        $parent.append(renderer.domElement);
        $parent.style.display = "show";
        callback();
        var controls = new OrbitControls(camera, parent);
        controls.target.x = camera.position.x;
        controls.target.y = camera.position.y;
        controls.target.z = 0;
        controls.zoomSpeed = 3;

        // Uncommend this to disable rotation (does not make much sense with 2D drawings).
        //controls.enableRotate = false;

        controls.addEventListener('change', () => {
            renderer.render(scene, camera);
        });
        renderer.render(scene, camera);
        controls.update();

        $parent.addEventListener('click', function (event) {
            var el = renderer.domElement;

            var rect = el.getBoundingClientRect();
            var x = ((event.pageX - rect.left) / rect.width) * 2 - 1;
            var y = -((event.pageY - rect.top) / rect.height) * 2 + 1;

            var vector = new THREE.Vector3(x, y, 0.5);
            vector.unproject(camera);

            var dir = vector.sub(camera.position).normalize();

            var distance = -camera.position.z / dir.z;

            var pos = camera.position.clone().add(dir.multiplyScalar(distance));
            scene.children.forEach(function (e) {
                e.visible = true;
            });
            renderer.render(scene, camera);
        });




        var originalWidth = renderer.domElement.width;
        var originalHeight = renderer.domElement.height;

        var hscale = width / originalWidth;
        var vscale = height / originalHeight;


        camera.top = (vscale * camera.top);
        camera.bottom = (vscale * camera.bottom);
        camera.left = (hscale * camera.left);
        camera.right = (hscale * camera.right);

        //        camera.updateProjectionMatrix();

        renderer.setSize(width, height);
        renderer.setClearColor(0xfffffff, 1);
        renderer.render(scene, camera);
        return false;
    });

}


export function createLineTypeShaders(data: BcsDrawing2dData) {
    // var ltype, type;
    // if (!data.tables || !data.tables.lineType) return;
    // var ltypes = data.tables.lineType.lineTypes;

    // for (type in ltypes) {
    //     ltype = ltypes[type];
    //     if (!ltype.pattern) continue;
    //     ltype.material = createDashedLineShader(ltype.pattern);
    // }
}

interface DashedLineShader {
    uniforms: any,
    vertexShader: any,
    fragmentShader: any,
}

export function createDashedLineShader(pattern: any) {//此方法暂时无用
    var i,
        totalLength = 0.0;

    for (i = 0; i < pattern.length; i++) {
        totalLength += Math.abs(pattern[i]);
    }

    const uniforms = THREE.UniformsUtils.merge([

        THREE.UniformsLib['common'],
        THREE.UniformsLib['fog'],

        {
            'pattern': { type: 'fv1', value: pattern },
            'patternLength': { type: 'f', value: totalLength }
        }

    ]);

    const vertexShader = [
        'attribute float lineDistance;',

        'varying float vLineDistance;',

        THREE.ShaderChunk['color_pars_vertex'],

        'void main() {',

        THREE.ShaderChunk['color_vertex'],

        'vLineDistance = lineDistance;',

        'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',

        '}'
    ].join('\n');

    const fragmentShader = [
        'uniform vec3 diffuse;',
        'uniform float opacity;',

        'uniform float pattern[' + pattern.length + '];',
        'uniform float patternLength;',

        'varying float vLineDistance;',

        THREE.ShaderChunk['color_pars_fragment'],
        THREE.ShaderChunk['fog_pars_fragment'],

        'void main() {',

        'float pos = mod(vLineDistance, patternLength);',

        'for ( int i = 0; i < ' + pattern.length + '; i++ ) {',
        'pos = pos - abs(pattern[i]);',
        'if( pos < 0.0 ) {',
        'if( pattern[i] > 0.0 ) {',
        'gl_FragColor = vec4(1.0, 0.0, 0.0, opacity );',
        'break;',
        '}',
        'discard;',
        '}',

        '}',

        THREE.ShaderChunk['color_fragment'],
        THREE.ShaderChunk['fog_fragment'],

        '}'
    ].join('\n');

    const dashedLineShader: DashedLineShader = {
        uniforms,
        vertexShader,
        fragmentShader
    }
    return dashedLineShader;
}



/**
 * 根据给定的实体、数据、字体和场景绘制图形。
 * @param entity 实体对象
 * @param data 图形数据
 * @param {Font} font 字体对象
 * @returns 图形对象或图形对象数组
 */
export function drawEntity(entity: Entity, data: BcsDrawing2dData, font: Font): THREE.Object3D[] {
    let object3dList: THREE.Object3D[] = [];

    // 根据实体类型选择相应的绘制函数
    if (entity.type === 'TSArc3d' || entity.type === 'TSCircle3d') {
        // 绘制弧线或圆
        const mesh = drawArc(entity as Arc | Circle, data);
        object3dList.push(mesh);
    } else if (entity.type === 'TSPolyline' || entity.type === 'POLYLINE') {
        const mesh = drawPolyline(entity as Polyline, data); // 绘制多段线或直线
        object3dList.push(mesh);
    } else if (entity.type === 'TSLine3d') {
        const mesh = drawLine_TSline(entity as TSLine3d, data); // 绘制三维线段
        object3dList.push(mesh);
    } else if (entity.type === 'TSAlignedDimension' || entity.type === 'TSRotatedDimension') {
        object3dList = drawLine_Aligned_or_RotationDimension(entity as TSAlignedDimension | TSRotatedDimension, data, font); // 绘制对齐或旋转标注
    } else if (entity.type === 'TEXT' || entity.type === 'TSText') {
        const mesh = drawText(entity as TSText, data, font); // 绘制文本
        object3dList.push(...mesh);
    }
    else {
        console.log("Unsupported Entity Type: " + entity.type); // 不支持的实体类型
    }

    // 设置绘制的图形对象的额外属性
    for (let i = 0; i < object3dList.length; i++) {
        object3dList[i].userData.layer = entity.layer; // 设置图层属性
        object3dList[i].userData.type = entity.type; // 设置类型属性
        if (entity.color) {
            object3dList[i].userData.color = entity.color; // 设置颜色属性
        }
    }

    return object3dList; // 返回绘制的图形对象或图形对象数组
}





/**
 * 根据给定的Arc实体和数据绘制弧线。
 * @param entity 弧线实体
 * @param {any} data 数据
 * @returns {THREE.Line} 弧线对象
 */
export function drawArc(entity: Arc | Circle, data: BcsDrawing2dData) {
    var endAngle, startAngle;

    // 如果实体类型为'TSCircle3D'
    if (entity.type === 'TSCircle3d') {
        startAngle = 0; // 获取起始角度，默认为0
        endAngle = 2 * Math.PI; // 计算终止角度，为起始角度加上2π
    } else {
        const arc = entity as Arc;
        startAngle = arc.StartAngle; // 获取起始角度
        endAngle = arc.EndAngle; // 获取终止角度
    }

    // 创建ArcCurve对象
    var curve = new THREE.ArcCurve(
        0, 0,
        entity.Radius,
        startAngle,
        endAngle
    );

    // 获取弧线上的点
    var points = curve.getPoints(64);

    // 创建BufferGeometry并设置点集合
    var geometry = new THREE.BufferGeometry().setFromPoints(points);

    // 创建LineBasicMaterial并设置颜色
    var material = new THREE.LineBasicMaterial({ color: getColor(entity, data) });

    // 创建Line对象
    var arc = new THREE.Line(geometry, material);

    // 设置弧线的位置
    arc.position.x = entity.Center.X;
    arc.position.y = entity.Center.Y;
    arc.position.z = entity.Center.Z;

    return arc; // 返回弧线对象
}


export function drawPolyline(entity: Polyline, data: BcsDrawing2dData) {
    //TS
    var /*geometry = new Geometry(),*/
        color = getColor(entity, data),
        material, vertex, startPoint, endPoint, bulgeGeometry,
        bulge, i /*,line*/;
    const points = [] as any;
    for (i = 0; i < entity.points.length; i++) {
        vertex = entity.points[i];
        points.push(new THREE.Vector3(vertex.X, vertex.Y, 0));
    }
    if (entity.closed) {
        points.push(new THREE.Vector3(entity.points[0].X, entity.points[0].Y, 0));
    }
    material = new THREE.LineBasicMaterial({ linewidth: 1, color: color });
    const geometry = new THREE.BufferGeometry().setFromPoints(points);
    const line = new THREE.Line(geometry, material);
    return line;
}

export function getColor(entity: Entity, data: BcsDrawing2dData) {
    var color = 0x000000; //default
    if (entity.color) color = entity.color;
    else if (data.tables && data.tables.layer && data.tables.layer.layers[entity.layer])
        color = data.tables.layer.layers[entity.layer].color;

    if (color == null || color === 0xffffff) {
        color = 0x000000;
    }
    return color;
}


export function drawLine_TSline(entity: TSLine3d, data: BcsDrawing2dData) {
    var geometry,
        color = getColor(entity, data),
        material, line;
    const points = [] as any;
    for (let key in entity) {
        if (key == 'StartPoint' || key == 'EndPoint') {
            const point = entity[key];
            points.push(new THREE.Vector3(point.X, point.Y, point.Z));
        }
    }
    geometry = new THREE.BufferGeometry().setFromPoints(points);
    material = new THREE.LineBasicMaterial({ linewidth: 100, color: color });
    line = new THREE.Line(geometry, material);
    return line;
}

export function drawLine_Aligned_or_RotationDimension(entity: TSAlignedDimension | TSRotatedDimension, data: any, font: Font) {
    var points = [] as any
    var points1 = [] as any
    var points2 = [] as any
    var points3 = []//标注斜线
    let color = getColor(entity, data)//0x00ff0000
    // color = 0x00ff0000
    const XLine1Point = entity.XLine1Point;
    const XLine2Point = entity.XLine2Point;
    const DimLinePoint = entity.DimLinePoint;
    const dimType = entity.type;  // TSRotatedDimension    TSAlignedDimension
    const DimensionText = entity.DimensionText;
    const Rotation = 'Rotation' in entity ? entity.Rotation : calculateRotationAngle(entity.XLine1Point, entity.XLine2Point) as any;
    // const Rotation = entity.Rotation

    // 计算得到尺寸横线上的另外一点
    const dimP2 = extendPointByRotation({ X: DimLinePoint.X, Y: DimLinePoint.Y }, Rotation, 1000);

    // 构造尺寸横线
    const line = { point: DimLinePoint, angle: Rotation };

    // 求 XLine1Point 到 尺寸横线的垂足 P1
    const P1 = footPointOfPointToSegment(entity.XLine1Point.X, entity.XLine1Point.Y, entity.DimLinePoint.X, entity.DimLinePoint.Y, dimP2.X, dimP2.Y)
    points.push(new THREE.Vector3(P1.X, P1.Y, 0));

    // 求 XLine2Point 到 尺寸横线的垂足 P2
    const P2 = footPointOfPointToSegment(entity.XLine2Point.X, entity.XLine2Point.Y, entity.DimLinePoint.X, entity.DimLinePoint.Y, dimP2.X, dimP2.Y);

    const geometry = new THREE.BufferGeometry().setFromPoints(points);
    let material = new THREE.LineBasicMaterial({ linewidth: 100, color: 0x00ff0000 });
    let line1 = new THREE.Line(geometry, material);

    let arr = [] as any;

    // 绘制小斜线1
    arr.push(invoking_createObliqueLine({ X: P1.X, Y: P1.Y }, Rotation, data_bcs!.scale, { linewidth: 100, color }))
    // 绘制小斜线2
    arr.push(invoking_createObliqueLine({ X: P2.X, Y: P2.Y }, Rotation, data_bcs!.scale, { linewidth: 100, color }))

    //画标注线
    points1.push(new THREE.Vector3(P1.X, P1.Y, 0));
    points1.push(new THREE.Vector3(P2.X, P2.Y, 0));
    const geometry1 = new THREE.BufferGeometry().setFromPoints(points1);
    let line2 = new THREE.Line(geometry1, material);

    let b = extendLine({ x: XLine2Point.X, y: XLine2Point.Y } as any, { x: P2.X, y: P2.Y } as any, data_bcs!.scale)
    let b_x = calculateXCoordinate(b.p3, b.p4, data_bcs!.scale)
    points2.push(new THREE.Vector3(b.p3.x, b.p3.y, 0));
    points2.push(new THREE.Vector3(b_x.x, b_x.y, 0));
    const geometry2 = new THREE.BufferGeometry().setFromPoints(points2);

    //计算尺寸标注长度
    let length = calculateDistance({ X: P1.X, Y: P1.Y }, { X: P2.X, Y: P2.Y }) as number
    let place = getMidPoint({ X: P1.X, Y: P1.Y }, { X: P2.X, Y: P2.Y })

    //标注长度文字写到场景中
    let tsText: TSText = {
        Position: {
            X: place.x,
            Y: place.y
        },
        TextString: Math.floor(length).toString(),
        HorizontalMode: HorizontalTextAlignment.Center,
        VerticalMode: VerticalTextAlignment.Bottom,
        // Rotation: calculateRotationAngle({ X: P1.X, Y: P1.Y }, { X: P2.X, Y: P2.Y }),
        Rotation: calculateRotationAngle({ X: P1.X, Y: P1.Y }, { X: P2.X, Y: P2.Y }),
        Height: 2.5 * data.scale,
        type: 'TSText',
    }
    let text = drawText(tsText, data, font)
    arr.push(...text)

    let line3 = new THREE.Line(geometry2, material);
    arr.push(line1)
    arr.push(line2)
    arr.push(line3)
    return arr;

}

/**
 * 获取易于阅读的文本旋转角度
 * @param dRotation - 待调整的旋转角度，以弧度表示
 * @returns 调整后的旋转角度，以弧度表示
 */
function getEasyToReadTextRotation(dRotation: number) {
    dRotation = standardAngle(dRotation);
    // 如果文本的旋转角度在 95° 到 275° 之间
    if (dRotation > (Math.PI / 180) * 95 && dRotation < (Math.PI / 180) * 275) {
        dRotation += Math.PI; // 将旋转角度增加 180°，使得文本倒置显示
    }
    return dRotation;
}

/**
 * 将角度标准化为 0 到 2π 的范围
 * @param dAngle - 待标准化的角度，以弧度表示
 * @returns 标准化后的角度，以弧度表示
 */
function standardAngle(dAngle: number) {
    while (dAngle < 0) {
        dAngle += Math.PI * 2; // 将角度调整到 0 到 360° 之间
    }
    while (dAngle >= Math.PI * 2) {
        dAngle -= Math.PI * 2; // 将角度调整到 0 到 360° 之间
    }
    return dAngle;
}




/**
 * 计算两个点的中间点坐标
 * @param pointA 点A的坐标，包含x和y属性
 * @param pointB 点B的坐标，包含x和y属性
 * @returns 两个点的中间点坐标，包含x和y属性
 */
function getMidPoint(pointA: Point2d, pointB: Point2d): THREE.Vec2 {
    // 计算中点的x坐标
    const midX = (pointA.X + pointB.X) / 2;
    // 计算中点的y坐标
    const midY = (pointA.Y + pointB.Y) / 2;
    // 返回中点坐标
    return { x: midX, y: midY };
}


/**
 * 计算两个二维点之间的距离(目前是计算标注线之间的距离)
 * @param point1 第一个点的坐标
 * @param point2 第二个点的坐标
 * @returns 两个点之间的距离
 */
function calculateDistance(point1: Point2d, point2: Point2d): number {
    const xDiff = point2.X - point1.X;
    const yDiff = point2.Y - point1.Y;
    const distance = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
    return distance;
}


/**
 * 创建斜线并返回线段对象
 * @param {Point2d} pt 起点
 * @param {number} Rotation 旋转角度，弧度制
 * @param {number} length 长度
 * @param {any} mate 材质
 * @returns {THREE.Line} 线段对象
 */
function invoking_createObliqueLine(pt: Point2d, Rotation: number, length: number, mate: any): THREE.Line {
    let material = new THREE.LineBasicMaterial(mate);
    const obliqueLine = createObliqueLine({ X: pt.X, Y: pt.Y }, Rotation, 2.0 * length);
    const points: Vector3[] = [];
    points.push(new THREE.Vector3(obliqueLine.startPoint.X, obliqueLine.startPoint.Y, 0));
    points.push(new THREE.Vector3(obliqueLine.endPoint.X, obliqueLine.endPoint.Y, 0));
    const geometry = new THREE.BufferGeometry().setFromPoints(points);
    let line = new THREE.Line(geometry, material);
    return line;
}


/**
 * 绘制尺寸标注的小斜线
 * @param pt 尺寸引出线与尺寸横线的交点
 * @param Rotation 尺寸横线的旋转角
 * @param length 小斜线的整体长度
 * @returns 
 */
function createObliqueLine(pt: Point2d, Rotation: number, length: number): Line2d {
    const angle = Rotation + Math.PI / 4;
    const startPoint = extendPointByRotation(pt, angle, -length / 2);
    const endPoint = extendPointByRotation(pt, angle, length / 2);

    return { startPoint, endPoint };
}

/**
 * 计算从点P到点Q的旋转角度（弧度）
 * @param pointP 点P的坐标
 * @param pointQ 点Q的坐标
 * @returns 旋转角的弧度值
 */
function calculateRotationAngle(pointP: Point2d, pointQ: Point2d): number {
    // 计算点Q相对于点P的水平距离
    const deltaX = pointQ.X as number - (pointP.X as number);
    // 计算点Q相对于点P的垂直距离
    const deltaY = pointQ.Y as number - (pointP.Y as number);
    // 使用Math.atan2函数计算旋转角的弧度值
    const radian = Math.atan2(deltaY, deltaX);
    return radian;
}


/**
 * 从pt点出发，沿着旋转角 angle 的方向，移动 distance 距离，得到新的点。
 * @param pt 一个点
 * @param angle 旋转角
 * @param distance 距离
 * @returns 新的点
 */
function extendPointByRotation(pt: Point2d, angle: number, distance: number): Point2d {
    const newX = pt.X + distance * Math.cos(angle);
    const newY = pt.Y + distance * Math.sin(angle);
    return { X: newX, Y: newY };
}
/**
 * 计算点(x0, y0)到线段(x1, y1) - (x2, y2)的垂足坐标
 * @param x0 点的x坐标
 * @param y0 点的y坐标
 * @param x1 线段起点的x坐标
 * @param y1 线段起点的y坐标
 * @param x2 线段终点的x坐标
 * @param y2 线段终点的y坐标
 * @returns 垂足坐标以及一个布尔值flag，表示垂足是否在线段上
 */
function footPointOfPointToSegment(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number): { X: number, Y: number, flag: boolean } {
    // 如果两点相同，则输出一个点的坐标为垂足
    if (x1 === x2 && y1 === y2) {
        return {
            X: x1,
            Y: y1,
            flag: true
        };
    }
    // 计算垂足在线段上的位置k
    const k = -((x1 - x0) * (x2 - x1) + (y1 - y0) * (y2 - y1)) / ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    // 计算垂足坐标
    const xf = k * (x2 - x1) + x1;
    const yf = k * (y2 - y1) + y1;
    // 判断垂足是否在线段上
    const flag = k >= 0 && k <= 1;

    return {
        X: xf,
        Y: yf,
        flag
    };
}

/**
 * 延长线段的函数
 * @param p1 线段起点的Vector2对象
 * @param p2 线段终点的Vector2对象
 * @param scale 延长的比例尺度
 * @returns 延长后的线段起点p3和终点p4的坐标
 */
function extendLine(p1: Vector2, p2: Vector2, scale: number): { p3: Vector2, p4: Vector2 } {
    // 将p1和p2转换为THREE.Vector2对象
    p1 = new THREE.Vector2(p1.x, p1.y);
    p2 = new THREE.Vector2(p2.x, p2.y);

    // 计算线段的方向向量
    const vec = p2.clone().sub(p1).normalize();

    // 计算延长后的线段起点p3和终点p4的坐标
    const p3 = p2.clone().add(vec.clone().multiplyScalar(2 * scale));
    const p4 = p1.clone().sub(vec.clone().multiplyScalar(2 * scale));

    return { p3: p3, p4: p4 };
}


/**
 * 根据给定线段AB的长度和标注线的长度，计算标注线上的新坐标
 * @param A 线段起点的Vector2对象
 * @param B 线段终点的Vector2对象
 * @param length 标注线的长度
 * @returns 标注线上的新坐标的x和y坐标值
 */
function calculateXCoordinate(A: Vector2, B: Vector2, length: number): { x: number, y: number } {
    // 计算A到B的距离
    const deltaX = B.x - A.x;
    const deltaY = B.y - A.y;
    const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);

    // 计算标注线长度与AB长度的比例尺度
    const ratio = (length * 4) / distance;

    // 计算标注线上的新坐标
    const newX = A.x + deltaX * ratio;
    const newY = A.y + deltaY * ratio;

    return { x: newX, y: newY };
}

/**
 * 绘制文本的函数
 * @param entity 实体对象
 * @param data 数据对象
 * @param font 字体对象
 * @returns 用于绘制文本的Three.js Mesh对象
 */
export function drawText(entity: TSText, data: BcsDrawing2dData, font: Font) {
    var material, text;
    let arr = [] as any
    let rotationAngle = entity.Rotation || 0;
    if (!font) {
        throw ('Text is not supported without a Three.js font loaded with THREE.FontLoader! Load a font of your choice and pass this into the constructor. See the sample for this repository or Three.js examples at http://threejs.org/examples/?q=text#webgl_geometry_text for more details.');
    }
    if (entity.TextString == '') {
        return []
    }
    var geometry = new TextGeometry(entity.TextString, { font: font, height: 0, curveSegments: 1, size: entity.Height || 12 }) as any;
    material = new THREE.MeshBasicMaterial({ color: getColor(entity, data) });
    text = new THREE.Mesh(geometry, material);

    text.position.x = entity.Position.X;
    text.position.y = entity.Position.Y;
    text.position.z = 0;

    // //拿到盒子实例
    var boundingBox = new THREE.Box3().setFromObject(text);
    // //拿到文本盒子的宽度
    // var textWidth = boundingBox.max.x - boundingBox.min.x;
    var textHeight = boundingBox.max.y - boundingBox.min.y;
    // //计算偏移量
    text.rotation.set(0, 0, getEasyToReadTextRotation(rotationAngle));
    // 将文本框基线对齐方式设置为居中

    geometry.translate(0, -textHeight, 0);
    geometry.scale(0.6, 0.6, 0.6);
    // 计算文本框的中心点坐标并进行偏移
    geometry.computeBoundingBox();
    var center = geometry.boundingBox.getCenter(new THREE.Vector3());
    geometry.center();
    geometry.translate(0, -center.y * 2, 0);

    var height = geometry.boundingBox.max.y - geometry.boundingBox.min.y;

    // var boxHelper = new THREE.BoxHelper(text, 0x800080);//画出围绕文字的盒子
    // arr.push(boxHelper)

    arr.push(text)
    return arr;

}


export function renderGeometry(obj: THREE.Object3D, layer_num: { [x: string]: number; }, res_entities: any, scene: THREE.Scene) {
    if (!layer_num[obj.userData.type]) {
        layer_num[obj.userData.type] = 0
    }
    layer_num[obj.userData.type]++
    switch (obj.userData.type) {
        case "LINE":
        case "LWPOLYLINE":
        case "TSPolyline":
        case "TSLine3d":
        case "MTEXT":
        case "TSText":
        case "TEXT":
        case "CIRCLE":
        case "TSCircle3d":
        case "TSAlignedDimension":
        case "TSRotatedDimension":
        case "SOLID":
        case "TSArc3d":
            scene.add(obj);
            break;
        case "ARC":
            scene.add(obj);
            break;
        default:
            scene.add(obj);
            break;
    }
}